// 包含项目头文件和系统头文件
#include "common.h"         // 包含项目公共定义
#include <stdio.h>          // 包含标准输入输出函数
#include <stdlib.h>         // 包含标准库函数
#include <string.h>         // 包含字符串处理函数
#include <unistd.h>         // 包含标准符号常量和类型
#include <errno.h>          // 包含错误号定义
#include <sys/stat.h>       // 包含数据返回结构体

/**
 * 主函数
 * 客户端程序入口，提供用户界面用于选择和发送任务到服务端
 * @return 程序退出状态码
 */
int main() {
    // 连接消息队列
    int msqid = msgget(MSG_QUEUE_KEY, 0666);                        // 尝试连接已存在的消息队列
    if (msqid == -1) {                                              // 如果连接失败
        // 如果连接失败，尝试创建消息队列
        msqid = msgget(MSG_QUEUE_KEY, IPC_CREAT | 0666);            // 创建新的消息队列
        if (msqid == -1) {                                          // 如果创建也失败
            perror("无法访问或创建消息队列");                       // 打印错误信息
            exit(EXIT_FAILURE);                                     // 退出程序
        }
    }
    
    // 准备管道读取端
    if (mkfifo(PIPE_PATH, 0666) == -1 && errno != EEXIST) {         // 创建命名管道，如果已存在则忽略错误
        perror("无法创建命名管道");                                 // 如果创建失败，打印错误信息
        exit(EXIT_FAILURE);                                         // 退出程序
    }
    
    printf("客户端已启动。已连接到服务器。\n");                     // 打印客户端启动信息
    
    while (1) {                                                     // 无限循环提供用户界面
        printf("\n选择任务类型:\n");                                // 打印菜单标题
        printf("1. 发送文件\n2. 存储字符串\n3. 查询文件\n4. 计算\n5. 查询任务\n6. 退出\n> ");  // 打印菜单选项
        
        int choice;                                                 // 定义用户选择变量
        if (scanf("%d", &choice) != 1) {                            // 读取用户选择，如果读取失败
            printf("输入无效。请输入数字。\n");                     // 打印错误信息
            // 清除输入缓冲区
            int c;                                                  // 定义字符变量
            while ((c = getchar()) != '\n' && c != EOF);            // 清空输入缓冲区
            continue;                                               // 继续下一次循环
        }
        getchar();                                                  // 消耗换行符
        
        if (choice == 6) break;                                     // 如果选择退出，则跳出循环
        
        TaskMessage msg = {.mtype = 1};                             // 初始化消息结构体，设置消息类型为1
        msg.task.sender_pid = getpid();                             // 设置发送方进程ID
        msg.task.status = TASK_PENDING;                             // 设置任务状态为待处理
        msg.task.start_time = 0;                                    // 初始化开始时间为0
        msg.task.end_time = 0;                                      // 初始化结束时间为0
        msg.task.exec_time = 0.0;                                   // 初始化执行时间为0
        memset(msg.task.data, 0, sizeof(msg.task.data));            // 清空任务数据字段
        memset(msg.task.result, 0, sizeof(msg.task.result));        // 清空任务结果字段
        
        switch (choice) {                                           // 根据用户选择分支处理
            case 1:                                                 // 处理发送文件选项
                msg.task.type = TASK_FILE_TRANSFER;                 // 设置任务类型为文件传输
                printf("文件信息 (格式: 文件名|文件内容，或仅文件名创建空文件): ");  // 提示用户输入
                if (fgets(msg.task.data, sizeof(msg.task.data), stdin) == NULL) {  // 读取用户输入
                    printf("读取输入时出错\n");                     // 如果读取失败，打印错误信息
                    continue;                                       // 继续下一次循环
                }
                // 移除末尾换行符
                msg.task.data[strcspn(msg.task.data, "\n")] = 0;    // 移除字符串末尾的换行符
                break;                                              // 跳出switch语句
                
            case 2:                                                 // 处理存储字符串选项
                msg.task.type = TASK_STRING_STORE;                  // 设置任务类型为字符串存储
                printf("要存储的字符串: ");                         // 提示用户输入
                if (fgets(msg.task.data, sizeof(msg.task.data), stdin) == NULL) {  // 读取用户输入
                    printf("读取输入时出错\n");                     // 如果读取失败，打印错误信息
                    continue;                                       // 继续下一次循环
                }
                // 移除末尾换行符
                msg.task.data[strcspn(msg.task.data, "\n")] = 0;    // 移除字符串末尾的换行符
                break;                                              // 跳出switch语句
                
            case 3:                                                 // 处理查询文件选项
                msg.task.type = TASK_QUERY_FILES;                   // 设置任务类型为查询文件
                break;                                              // 跳出switch语句
                
            case 4:                                                 // 处理计算选项
                msg.task.type = TASK_COMPUTE;                       // 设置任务类型为计算
                printf("表达式 (格式: 数字 运算符 数字，例如: 5 + 3): ");  // 提示用户输入
                if (fgets(msg.task.data, sizeof(msg.task.data), stdin) == NULL) {  // 读取用户输入
                    printf("读取输入时出错\n");                     // 如果读取失败，打印错误信息
                    continue;                                       // 继续下一次循环
                }
                // 移除末尾换行符
                msg.task.data[strcspn(msg.task.data, "\n")] = 0;    // 移除字符串末尾的换行符
                break;                                              // 跳出switch语句
                
            case 5:                                                 // 处理查询任务选项
                msg.task.type = TASK_QUERY_TASKS;                   // 设置任务类型为查询任务
                break;                                              // 跳出switch语句
                
            default:                                                // 处理无效选项
                printf("选择无效。请选择有效选项。\n");             // 打印错误信息
                continue;                                           // 继续下一次循环
        }
        
        // 发送任务到服务端
        if (msgsnd(msqid, &msg, sizeof(Task), 0) == -1) {           // 向消息队列发送消息
            perror("发送消息失败");                                 // 如果发送失败，打印错误信息
            continue;                                               // 继续下一次循环
        }
        
        // 等待服务端返回结果
        printf("等待结果...\n");                                    // 提示用户等待结果
        int pipe_fd = open(PIPE_PATH, O_RDONLY);                    // 以只读模式打开命名管道
        if (pipe_fd == -1) {                                        // 如果打开失败
            perror("无法打开管道进行读取");                         // 打印错误信息
            continue;                                               // 继续下一次循环
        }
        
        Task result;                                                // 定义结果任务结构体
        ssize_t bytes_read = read(pipe_fd, &result, sizeof(Task));  // 从管道读取结果
        close(pipe_fd);                                             // 关闭管道
        
        if (bytes_read == -1) {                                     // 如果读取失败
            perror("从管道读取失败");                               // 打印错误信息
            continue;                                               // 继续下一次循环
        } else if (bytes_read == 0) {                               // 如果未读取到数据
            printf("未从服务器收到数据\n");                         // 打印提示信息
            continue;                                               // 继续下一次循环
        }
        
        printf("结果: %s\n", result.result);                        // 打印任务执行结果
    }
    
    printf("客户端正在关闭。\n");                                   // 打印客户端关闭信息
    return 0;                                                       // 返回程序退出状态码
}